#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <signal.h>
#include <sys/epoll.h>
#include <stdlib.h>

/* 参考man epoll */

#define MAX_EVENTS 10

int main(int argc,char **argv)
{ 
    struct epoll_event ev, events[MAX_EVENTS];
    int nfds = 0, epollfd = 0, fd = 0, n = 0;

    char val = 0;
    int ret = 0;

    fd = open("/dev/eint_key", O_RDONLY);
    if (fd < 0) {
        printf("open failed \n");
        return -1;
    }

    epollfd = epoll_create1(0);
    if (epollfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
    /**
     * EPOLLET
epoll处理的事件有两种模式：边沿触发（edge-triggered）与水平触发（level-triggered）。
考虑以下情形：
1.我们向epoll实例注册一个文件描述符rfd，其代表某个管道的读端。我们关心其是否已经可读
2.从写端往管道里写了2KB数据
3.我们使用epoll_wait获得触发了的事件，其中包括我们事先注册的rfd
4.我们从rfd读取了1KB数据
5.我们再次使用epoll_wait
如果是边沿触发模式，那么epoll只会在第3步的epoll_wait中给出rfd被触发的事件，
第5步则不会给出相应的事件；如果是水平触发，那么epoll在第3步和第5步都会给出相应的事件。

0->1 发生这过程, 边沿触发,才得到fd
也就是说，只有在相应的文件描述符状态发生变化，从别的状态变成我们感兴趣的状态时，
“边沿触发”才会给出我们相应的事件；

只要是1,水平触发,能一直到底fd
只要相应的文件描述符处于感兴趣的状态时，
“水平触发”就会给出我们相应的事件。

边沿触发的的模式:
因此，当我们使用边沿触发模式时，我们的read或write操作不能只使用一次，
因为之后相关的事件就不会被触发，也就不能读取或写入完整的数据了。
我们应当在循环中使用read或write，直到其返回错误EAGAIN（详见open）。
同时由于EAGAIN错误只有在使用O_NONBLOCK标志位打开文件时才会出现，

所以我们在使用边沿触发时要注意两点：
使用非阻塞的文件描述符
read和write要一直读取或写入到返回EAGAIN错误

如果我们的events包含标志位EPOLLET，则该事件是边沿触发模式；

如果不包含该标志位，则该事件是水平触发模式。
     *
     */
    ev.events = EPOLLIN;
    ev.data.fd = fd;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        perror("epoll_ctl: listen_sock");
        exit(EXIT_FAILURE);
    }

    while(1) {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, 500); // 500ms, -1表示阻塞
        if (nfds == -1) {
            /* 小于0时,可以获取 */
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        } else if (nfds == 0) {
            /* 驱动中返回0 */
            printf("timeout\n");
        } else {
            for (n = 0; n < nfds; ++n) {
                if (events[n].data.fd == fd) {
                    ret = read(fd, &val, 1);
                    printf("val = %x\n", val);
                }
            }
        }
    }

    // close前要先从epoll中移除
    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
    close(fd);

    return 0;
}